import { View, ScrollView, Block } from '@tarojs/components'
import { useEffect, useRef, useState } from 'react'
import { getCurrentInstance, nextTick } from '@tarojs/taro'

import { useUpdateEffect } from '@/common/hooks'

import { formatList, setHeight } from './constant'

/**
 * @typedef {Object} VirtualListParam
 * @param	{Array}	list  列表数据
 * @param	{Number}	segmentNum  自定义分段的数量，默认10
 * @param	{Object}	scrollViewProps  scrollView的参数
 * @param	{Function}	onComplete  二维列表是否已经把全部数据加载完成的回调
 * @param	{Function}	onRender  二维列表Item的渲染回调
 * @param	{Function}	onRenderBottom  二维列表下部分内容渲染回调
 * @param	{Number}	screenNum  指定页面显示区域基准值，默认2
 * @param	{string}	listId  自定义id，默认值 zt-virtual-list 以防没传入
 */

/**
 * 虚拟列表
 *
 * @param {VirtualListParam} params - 虚拟列表所需的对象入参
 */
const VirtualList = ({
  list,
  segmentNum = 10,
  onComplete = () => {},
  onRender,
  scrollViewProps,
  onRenderBottom = () => <></>,
  className,
  listId = 'zt-virtual-list',
  screenNum
}) => {
  const [twoDimensionList, setTwoDimensionList] = useState<any[]>([]) // 二维数组
  const [isComplete, setIsCompelete] = useState(false) // 列表是否完成
  const [wholePageIndex, setWholePageIndex] = useState(0) // 相当于页数，每页的索引
  const [innerScrollTop, setInnerScrollTop] = useState(0) // 记录组件内部滚动高度

  const initList = useRef<any[]>([]) // 承载初始化的二维数组
  const pageHeight = useRef<any[]>([]) // 存储每个维度渲染之后所占的高度
  const currentInstance = useRef(getCurrentInstance()) // 当前组件的实例

  useEffect(() => {
    formatList(list, segmentNum, handleComplete, setInitList, setTwoDimensionList)
  }, [innerScrollTop])

  useUpdateEffect(() => {
    isComplete && onComplete && onComplete()
  }, [isComplete])

  useUpdateEffect(() => {
    // 类似于 Vue 中的 nextTick，收集列表渲染完之后的高度
    nextTick(() => {
      setHeight(
        list,
        wholePageIndex,
        listId,
        setPageHeight,
        scrollViewProps,
        screenNum,
        currentInstance.current,
        twoDimensionList,
        setTwoDimensionList,
        pageHeight.current,
        initList.current
      )
    })
  }, [twoDimensionList])

  useUpdateEffect(() => {
    pageHeight.current = []

    if (!list?.length) {
      handleComplete()
    }

    // 提前把 innerScrollTop 设置为与 0 不同，防止列表置顶失效
    setTwoDimensionList([])
    setIsCompelete(false)
    setWholePageIndex(0)
    setInnerScrollTop(0.1)
  }, [list])

  /**
   * 数据渲染完全
   */
  const handleComplete = () => {
    setIsCompelete(true)
  }

  const setInitList = (value) => {
    initList.current = value
  }

  const setPageHeight = (value: never) => {
    pageHeight.current = [...pageHeight.current, value]
  }

  /**
   * 监听触底回调
   */
  const renderNext = () => {
    const page_index = wholePageIndex + 1
    // 当所有数据完成加载之后不再加一
    if (!initList.current[page_index]?.length) {
      handleComplete()
      return
    }

    // 提供自己触底回调想做的事
    scrollViewProps?.onScrollToLower?.()

    // 更新对应维度的数据
    setTwoDimensionList((prevList) => {
      const newList = [...prevList]
      newList[page_index] = initList.current[page_index]
      return newList
    })

    setWholePageIndex(page_index)
  }

  const _scrollViewProps = {
    ...scrollViewProps,
    scrollTop: innerScrollTop === 0 ? 0 : ''
  }

  return (
    <ScrollView
      scrollY
      id={listId}
      style={{ height: '100%' }}
      className={`zt-virtual-list-container ${className}`}
      onScrollToLower={renderNext}
      {..._scrollViewProps}
    >
      <View className='zt-main-list'>
        {twoDimensionList.map((item: any, pageIndex) => (
          <View key={pageIndex} className={`wrap_${pageIndex}`}>
            {item?.length > 0 ? (
              <Block>
                {item.map((el, index) => {
                  return onRender?.(el, pageIndex * segmentNum + index, pageIndex)
                })}
              </Block>
            ) : (
              <View style={{ height: `${item?.height}px` }}></View>
            )}
          </View>
        ))}
      </View>
      {isComplete && onRenderBottom && onRenderBottom()}
    </ScrollView>
  )
}

export default VirtualList

VirtualList.defaultProps = {
  list: [],
  segmentNum: 10,
  scrollViewProps: {},
  className: '',
  screenNum: 2,
  listId: 'zt-virtial-list',
  onRender: function render() {
    return <View />
  }
}
